
#include "NH5Export.h"

#include <fstream>
#include <iostream>
#include <string>

#include <H5Lpublic.h>  // 引入 H5Lmove 函数的头文件
#include <highfive/highfive.hpp>

#include "Utility/Logging/SGLogger.h"

#include "DataStructure/Common/Integer.h"

#include "DBManager/Database/DatabaseFactory.h"
#include "DBManager/IService/Result/IResultDomainsService.h"
#include "DBManager/IService/Result/Nodal/IDISPLACEMENTService.h"
#include "DBManager/IService/Result/Nodal/IDISPLACEMENT_CPLXService.h"

#ifdef SG_USE_MPI
#include "Utility/Algebra/MpiEnvironment.h"
#endif

USING_EXPORT_NH5_NAMESPACE

using namespace SG::DBManager;
bool SG::Export::NH5Export::WriterNastranHDF5 (DBServiceFactorySP&             mainDBM,
                                               SG::DataStructure::FEM::Project project,
                                               const std::string&              H5filename)
{
#ifdef SG_USE_MPI
    /** 1）	在主程序的NH5的程序中，打开主数据库，
        然后遍历rank0里的所有 DomainId，（遍历检查一下所有数据的库的DomainId表吧，是否条数都一致）
        将rank0的DomainId一条条的都插入到总数据库中。

     2）	遍历所有数据库，根据DomainId遍历所有的位移数据表,然后将其插入至主数据库的位移表中。
        每次插入数据的时候需要更新DOMAIN_INDEX
        然后只对当前插入的数据排下序吧，后面测试下不排序对渲染的影响。

        这样的位移的结果就OK了。

     3）遍历所有数据库，根据DomainId遍历所有的应力、应变、特征向量数据表,然后将其插入至主数据库的位移表中。
        每次插入数据的时候需要更新DOMAIN_INDEX
        然后只对当前插入的数据排下序吧，后面测试下不排序对渲染的影响。

        这样的位移的结果就OK了。

    in: 分数据库 主数据库
    out:主数据库 */
    // dbm为主数据库

    SG::Algebra::MpiEnvironment mpiEnv (0, nullptr);

    // 连接所有数据库
    DatabaseFactory                                  dbFactory;
    std::map<int, IDatabase*>                        dbMap;
    std::map<int, std::shared_ptr<DBServiceFactory>> DBMMap;
    std::vector<std::shared_ptr<DBServiceFactory>>   DBMVec;

    for (int num = 0; num < mpiEnv.world_size (); num++)
    {
        auto dbx =
            dbFactory.GetDatabase (DatabaseFactory::HDF5, project.m_workSpace, project.m_jobName + "_" + std::to_string (num) + ".db");
        dbMap.insert ({ num, dbx });
        auto dbf = std::make_shared<DBServiceFactory> (dbx);
        DBMMap.insert ({ num, dbf });
        DBMVec.push_back (dbf);
    }

    // 获取domain表数据
    auto domainService0 = DBMMap[0]->get<IResultDomainsService> ();
    auto domainIds      = domainService0->GetAllId ();
    auto domainSize0    = domainIds.size ();

    for (int i = 1; i < mpiEnv.world_size (); i++)
    {
        auto domainSize = DBMMap[i]->get<IResultDomainsService> ()->GetAllId ().size ();
        if (domainSize0 != domainSize)
        {
            SGError << "On MPI: Domain Size is not equal! " << i << " with  0";
            return false;
        }
    }
    auto domainService = mainDBM->get<IResultDomainsService> ();
    for (auto&& i : domainService0->GetAllData ())
    {
        auto& nonConstDomain = const_cast<SG::DataStructure::Post::ResultDomains&> (i);
        domainService->Add (nonConstDomain);
    }

    std::vector<IDatabase::FunCopyDataRes> crfuncs = mainDBM->GetDatabase ()->CopyDataResFunVec ();
    for (IDatabase::FunCopyDataRes crfunc : crfuncs)
    {
        crfunc (DBMVec, mainDBM, domainIds);
    }

    // TODO(wxc) 对特征值的合并

#endif
    // 断开主数据库连接
    // 然后使用hdf5底层的api对数据进行操作

    mainDBM->DisconnectDatabase ();

    // 自己的数据库文件
    const hid_t SGFile_ID = H5Fopen (mainDBM->GetDatabase ()->GetFullpath ().c_str (), H5F_ACC_RDONLY, H5P_DEFAULT);
    // patran 可以识别的格式文件 -> 输出文件
    const hid_t H5file_ID = H5Fcreate (H5filename.c_str (), H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);

    // 判断是否存在"/INDEX"路径
    if (H5Lexists (SGFile_ID, "/INDEX", H5P_DEFAULT) != 0)
    {
        H5Ocopy (SGFile_ID, "/INDEX", H5file_ID, "/INDEX", H5P_DEFAULT, H5P_DEFAULT);

        if (H5Lexists (H5file_ID, "/INDEX/SG", H5P_DEFAULT) != 0)
        {
            H5Lmove (H5file_ID, "/INDEX/SG", H5file_ID, "/INDEX/NASTRAN", H5P_DEFAULT, H5P_DEFAULT);
        }
    }

    if (H5Lexists (SGFile_ID, "/SG", H5P_DEFAULT) != 0)
    {
        H5Ocopy (SGFile_ID, "/SG", H5file_ID, "/NASTRAN", H5P_DEFAULT, H5P_DEFAULT);
    }

    // 使用hdf5 c接口， 为H5file_ID添加属性PRECISION、 SCHEMA
    {
        hsize_t dims[1] = { 1 };
        auto dataspace_id    = H5Screate_simple (1, dims, NULL);
        // 添加属性PRECISION
        hid_t   attr_id   = H5Acreate2 (H5file_ID, "PRECISION", H5T_NATIVE_INT64,dataspace_id, H5P_DEFAULT, H5P_DEFAULT);
        int64_t precision = 64;
        H5Awrite (attr_id, H5T_NATIVE_INT64, &precision);
        H5Aclose (attr_id);

        // 添加属性SCHEMA
        attr_id         = H5Acreate2 (H5file_ID, "SCHEMA", H5T_NATIVE_INT64, dataspace_id, H5P_DEFAULT, H5P_DEFAULT);
        int64_t version = 20241;
        H5Awrite (attr_id, H5T_NATIVE_INT64, &version);
        H5Aclose (attr_id);
    }
    H5Fclose (SGFile_ID);
    H5Fclose (H5file_ID);

    mainDBM->ConnectDatabase ();

    return true;
}
